// 用法:
//  1) 处理单文件: node remove_js_comments.js path/file.js out/file.js
//  2) 覆盖单文件:  node remove_js_comments.js path/file.js --in-place
//  3) 递归处理目录(就地覆盖): node remove_js_comments.js path/dir
//  4) 递归处理目录输出到新目录: node remove_js_comments.js path/src path/dist
//  可选参数: --ext=.js,.mjs  指定扩展 (默认 .js)
//  注意: 输出目录若存在会复写同名文件; 未指定输出目录时目录模式为原地覆盖

const { readFileSync, writeFileSync, statSync, mkdirSync, existsSync, readdirSync } = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const generate = require('@babel/generator').default;

const argv = process.argv.slice(2);
if (argv.length === 0) {
  printUsage('缺少输入路径');
}

let [inputPath, secondArg, ...rest] = argv;
let outputBase = null;
let inPlace = false;
let keepLicense = false; // 保留开头/*! 的版权注释
let forceReformat = false; // 使用原来的 Babel 重新生成（会格式化）
let exts = ['.js'];

// 解析可选参数（健壮处理）
const extraArgs = [secondArg, ...rest].filter(Boolean);
for (let i = 0; i < extraArgs.length; i++) {
  const arg = extraArgs[i];
  if (!arg) continue;
  if (arg === '--in-place') { inPlace = true; continue; }
  if (arg === '--keep-license') { keepLicense = true; continue; }
  if (arg === '--reformat') { forceReformat = true; continue; }
  if (arg.indexOf('--ext=') === 0) {
    const list = arg.slice(6).split(',').map(s => s.trim()).filter(Boolean);
    if (list.length) exts = list.map(e => e.charAt(0) === '.' ? e : '.' + e);
    continue;
  }
  if (arg.charAt(0) !== '-') {
    outputBase = arg;
  }
}

function printUsage(msg) {
  if (msg) console.error('[Error]', msg); // 中文提示
  console.log('\nUsage:\n  node remove_js_comments.js <file.js> <out.js>\n  node remove_js_comments.js <file.js> --in-place\n  node remove_js_comments.js <dir> [outDir] [--ext=.js,.mjs] [--in-place]\n\nExamples:\n  node remove_js_comments.js src/a.js dist/a.clean.js\n  node remove_js_comments.js src/a.js --in-place\n  node remove_js_comments.js src/theme/default/assets\n  node remove_js_comments.js src theme_clean --ext=.js,.mjs\n');
  process.exit(msg ? 1 : 0);
}

// 校验输入
if (!existsSync(inputPath)) {
  printUsage(`输入路径不存在: ${inputPath}`);
}

const inputStat = statSync(inputPath);

// 如果是文件模式
if (inputStat.isFile()) {
  handleSingleFile(inputPath, outputBase, inPlace);
  process.exit(0);
}

function processFile(inFile, outFile, copyWhenSame) {
  try {
    const code = readFileSync(inFile, 'utf8');
    if (!/\/\/|\/\*/.test(code)) {
      if (!inPlace && copyWhenSame) writeFileSync(outFile, code, 'utf8');
      return 'skip';
    }
    if (forceReformat) {
      const ast = parser.parse(code, {
        sourceType: 'unambiguous',
        plugins: ['jsx','typescript','classProperties','decorators-legacy','dynamicImport'],
      });
      const output = generate(ast, { comments: false, compact: false }, code).code;
      writeFileSync(outFile, output, 'utf8');
    } else {
      const ast = parser.parse(code, {
        sourceType: 'unambiguous',
        plugins: ['jsx','typescript','classProperties','decorators-legacy','dynamicImport'],
        allowReturnOutsideFunction: true,
        errorRecovery: true,
      });
      const comments = ast.comments || [];
      if (!comments.length) {
        if (!inPlace && copyWhenSame) writeFileSync(outFile, code, 'utf8');
        return 'skip';
      }
      const removable = comments.filter(c => {
        if (keepLicense && c.type === 'CommentBlock' && code.slice(c.start, c.start + 3) === '/*!') return false;
        return true;
      });
      if (!removable.length) {
        if (!inPlace && copyWhenSame) writeFileSync(outFile, code, 'utf8');
        return 'skip';
      }
      const chars = code.split('');
      removable.sort((a,b)=> b.start - a.start).forEach(c => {
        for (let i = c.start; i < c.end; i++) {
          const ch = chars[i];
          if (ch !== '\n' && ch !== '\r') chars[i] = '';
        }
      });
      const stripped = chars.join('');
      writeFileSync(outFile, stripped, 'utf8');
    }
    console.log(`✔ ${inFile} -> ${outFile}`);
    return 'ok';
  } catch (e) {
    console.error(`✖ 解析失败: ${inFile} (${e.message})`);
    return 'fail';
  }
}

function handleSingleFile(inFile, outFile, inPlaceFlag) {
  if (inPlaceFlag && outFile) console.warn('警告: 单文件 in-place 模式忽略输出路径');
  const dest = inPlaceFlag ? inFile : outFile;
  if (!dest) printUsage('单文件模式需要输出文件或 --in-place');
  const res = processFile(inFile, dest, false);
  if (res === 'ok') console.log(`Done: ${inFile} -> ${dest}`); else if (res === 'skip') console.log(`Skip(无注释): ${inFile}`);
}

// 目录模式
if (!inputStat.isDirectory()) {
  printUsage('输入既不是文件也不是目录');
}

if (!inPlace && !outputBase) {
  // 目录模式未指定 output 则默认原地覆盖
  inPlace = true;
}

if (outputBase && !inPlace) {
  if (!existsSync(outputBase)) mkdirSync(outputBase, { recursive: true });
}

let fileCount = 0;
let okCount = 0;
let skipCount = 0;
let failCount = 0;

walkDir(inputPath, (abs, rel) => {
  if (!exts.includes(path.extname(abs))) return; // 扩展过滤
  fileCount++;
  const dest = inPlace ? abs : path.join(outputBase, rel);
  if (!inPlace) {
    const dirName = path.dirname(dest);
    if (!existsSync(dirName)) mkdirSync(dirName, { recursive: true });
  }
  const res = processFile(abs, dest, !inPlace);
  if (res === 'ok') okCount++; else if (res === 'skip') skipCount++; else failCount++;
});

console.log(`处理完成: 总文件:${fileCount} 成功:${okCount} 跳过(无注释):${skipCount} 失败:${failCount}`);

function walkDir(root, cb, prefix = '') {
  const list = readdirSync(root, { withFileTypes: true });
  list.forEach(dirent => {
    const abs = path.join(root, dirent.name);
    const rel = path.join(prefix, dirent.name);
    if (dirent.isDirectory()) {
      walkDir(abs, cb, rel);
    } else if (dirent.isFile()) {
      cb(abs, rel);
    }
  });
}
